{
  "cells": [
    {
      "metadata": {
        "id": "zWgEkOAO9OVz"
      },
      "cell_type": "markdown",
      "source": [
        "# Day in Riverbend Example\n",
        "\n",
        "An illustrative social simulation with 5 players which simulates a normal day in an imaginary town caller Riverbend. Each player has their own configurable backstory. The agents are configured to re-implement the architecure in Park et al. (2023) - they have reflection, plan, and identity components; their associative memory uses importance function. This is _not_ an exact re-implementation.\n",
        "\n",
        "Park, J.S., O'Brien, J.C., Cai, C.J., Morris, M.R., Liang, P. and Bernstein, M.S., 2023. Generative agents: Interactive simulacra of human behavior. arXiv preprint arXiv:2304.03442."
      ]
    },
    {
      "metadata": {
        "id": "RkDSo0jL8KFm"
      },
      "cell_type": "markdown",
      "source": [
        "\u003ca href=\"https://colab.research.google.com/github/google-deepmind/concordia/blob/main/examples/village/day_in_riverbend.ipynb\" target=\"_parent\"\u003e\u003cimg src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/\u003e\u003c/a\u003e"
      ]
    },
    {
      "metadata": {
        "id": "n2oBFybH6LRz"
      },
      "cell_type": "markdown",
      "source": [
        "## Setup and imports"
      ]
    },
    {
      "metadata": {
        "id": "tkCJgbFFvaAB"
      },
      "cell_type": "code",
      "source": [
        "# @title Colab-specific setup (use a CodeSpace to avoid the need for this).\n",
        "try:\n",
        "  %env COLAB_RELEASE_TAG\n",
        "except:\n",
        "  pass  # Not running in colab.\n",
        "else:\n",
        "  %pip install --ignore-requires-python --requirement 'https://raw.githubusercontent.com/google-deepmind/concordia/main/examples/requirements.in' 'git+https://github.com/google-deepmind/concordia.git#egg=gdm-concordia'\n",
        "  %pip list"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "-qLG5ExLqpWa"
      },
      "cell_type": "code",
      "source": [
        "# @title Imports\n",
        "\n",
        "import collections\n",
        "import concurrent.futures\n",
        "import datetime\n",
        "import random\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "from IPython import display\n",
        "import sentence_transformers\n",
        "\n",
        "from concordia.agents.deprecated import deprecated_agent as basic_agent\n",
        "from concordia.components import deprecated as generic_components\n",
        "from concordia.associative_memory.deprecated import associative_memory\n",
        "from concordia.associative_memory.deprecated import blank_memories\n",
        "from concordia.associative_memory.deprecated import formative_memories\n",
        "from concordia.associative_memory.deprecated import importance_function\n",
        "from concordia.clocks import game_clock\n",
        "from concordia.components.game_master import deprecated as gm_components\n",
        "from concordia.components.agent.deprecated import to_be_deprecated as components\n",
        "from concordia.environment.deprecated import game_master\n",
        "from concordia.contrib.language_models.openai import gpt_model\n",
        "from concordia.deprecated.metrics import goal_achievement\n",
        "from concordia.deprecated.metrics import common_sense_morality\n",
        "from concordia.deprecated.metrics import opinion_of_others\n",
        "from concordia.utils import html as html_lib\n",
        "from concordia.utils.deprecated import measurements as measurements_lib\n",
        "from concordia.utils import plotting\n",
        "\n"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "I3OtW8flCJSC"
      },
      "cell_type": "code",
      "source": [
        "# @title Setup sentence encoder\n",
        "st_model = sentence_transformers.SentenceTransformer(\n",
        "    'sentence-transformers/all-mpnet-base-v2')\n",
        "embedder = lambda x: st_model.encode(x, show_progress_bar=False)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "cugwvFIKv5AS"
      },
      "cell_type": "code",
      "source": [
        "# @title Language Model - pick your model and provide keys\n",
        "\n",
        "# By default this colab uses GPT-4, so you must provide an API key.\n",
        "# Note that it is also possible to use local models or other API models,\n",
        "# simply replace this cell with the correct initialization for the model\n",
        "# you want to use.\n",
        "GPT_API_KEY = '' #@param {type: 'string'}\n",
        "GPT_MODEL_NAME = 'gpt-4o' #@param {type: 'string'}\n",
        "\n",
        "if not GPT_API_KEY:\n",
        "  raise ValueError('GPT_API_KEY is required.')\n",
        "\n",
        "model = gpt_model.GptLanguageModel(api_key=GPT_API_KEY,\n",
        "                                   model_name=GPT_MODEL_NAME)\n"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "z9HYjZgyakc_"
      },
      "cell_type": "markdown",
      "source": [
        "## Configuring the generic knowledge of players and GM."
      ]
    },
    {
      "metadata": {
        "id": "cVfExQ0suX5j"
      },
      "cell_type": "code",
      "source": [
        "#@title Make importance models\n",
        "\n",
        "importance_model = importance_function.AgentImportanceModel(model)\n",
        "importance_model_gm = importance_function.ConstantImportanceModel()\n"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "TeVYseoD2WYa"
      },
      "cell_type": "code",
      "source": [
        "#@title Make the clock\n",
        "SETUP_TIME = datetime.datetime(hour=8, year=2024, month=9, day=1)\n",
        "\n",
        "START_TIME = datetime.datetime(hour=9, year=2024, month=10, day=1)\n",
        "clock = game_clock.MultiIntervalClock(\n",
        "    start=SETUP_TIME,\n",
        "    step_sizes=[datetime.timedelta(hours=1), datetime.timedelta(seconds=10)])\n"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "b8vWoQ6by51N"
      },
      "cell_type": "code",
      "source": [
        "# @title Generic memories are memories that all players and GM share.\n",
        "\n",
        "shared_memories = [\n",
        "    'There is a hamlet named Riverbend.',\n",
        "    'Riverbend is an idyllic rural town.',\n",
        "    'The river Solripple runs through the village of Riverbend.',\n",
        "    'The Solripple is a mighty river.',\n",
        "    'Riverbend has a temperate climate.',\n",
        "    'Riverbend has a main street.',\n",
        "    'There is a guitar store on Main street Riverbend.',\n",
        "    'There is a grocery store on Main street Riverbend.',\n",
        "    'There is a school on Main street Riverbend.',\n",
        "    'There is a library on Main street Riverbend.',\n",
        "    'Riverbend has only one pub.',\n",
        "    'There is a pub on Main street Riverbend called The Sundrop Saloon.',\n",
        "    'Town hall meetings often take place at The Sundrop Saloon.',\n",
        "    'Riverbend does not have a park',\n",
        "    'The main crop grown on the farms near Riverbend is alfalfa.',\n",
        "    'Farms near Riverbend depend on water from the Solripple river.',\n",
        "]\n",
        "\n",
        "# The generic context will be used for the NPC context. It reflects general\n",
        "# knowledge and is possessed by all characters.\n",
        "shared_context = model.sample_text(\n",
        "    'Summarize the following passage in a concise and insightful fashion:\\n'\n",
        "    + '\\n'.join(shared_memories)\n",
        "    + '\\n'\n",
        "    + 'Summary:'\n",
        ")\n",
        "print(shared_context)\n"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "YBCXUQ8sayzj"
      },
      "cell_type": "markdown",
      "source": [
        "## Functions to build the players"
      ]
    },
    {
      "metadata": {
        "id": "T41TQHB7vacw"
      },
      "cell_type": "code",
      "source": [
        "# @title setup formative memory factories\n",
        "blank_memory_factory = blank_memories.MemoryFactory(\n",
        "    model=model,\n",
        "    embedder=embedder,\n",
        "    importance=importance_model.importance,\n",
        "    clock_now=clock.now,\n",
        ")\n",
        "formative_memory_factory = formative_memories.FormativeMemoryFactory(\n",
        "    model=model,\n",
        "    shared_memories=shared_memories,\n",
        "    blank_memory_factory_call=blank_memory_factory.make_blank_memory,\n",
        ")"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "As465DbcsAwZ"
      },
      "cell_type": "code",
      "source": [
        "def build_agent(\n",
        "    agent_config,\n",
        "    player_names: list[str],\n",
        "    measurements: measurements_lib.Measurements | None = None,\n",
        "):\n",
        "  mem = formative_memory_factory.make_memories(agent_config)\n",
        "\n",
        "  # Build the player.\n",
        "\n",
        "  time = components.report_function.ReportFunction(\n",
        "      name='current_time', function=clock.current_time_interval_str\n",
        "  )\n",
        "\n",
        "  identity = components.identity.SimIdentity(model, mem, agent_config.name)\n",
        "  goal_component = generic_components.constant.ConstantComponent(\n",
        "      state=agent_config.goal\n",
        "  )\n",
        "  reflection = components.reflection.Reflection(\n",
        "      model=model,\n",
        "      memory=mem,\n",
        "      agent_name=agent_config.name,\n",
        "      importance_threshold=15.0,\n",
        "      verbose=False,\n",
        "  )\n",
        "  plan = components.plan.SimPlan(\n",
        "      model,\n",
        "      mem,\n",
        "      agent_config.name,\n",
        "      clock_now=clock.now,\n",
        "      components=[identity, time],\n",
        "      goal=goal_component,\n",
        "      verbose=False,\n",
        "  )\n",
        "  current_obs = components.observation.Observation(\n",
        "            agent_name=agent_config.name,\n",
        "      clock_now=clock.now,\n",
        "      memory=mem,\n",
        "      timeframe=clock.get_step_size(),\n",
        "      component_name='current observations',\n",
        "  )\n",
        "  summary_obs = components.observation.ObservationSummary(\n",
        "      agent_name=agent_config.name,\n",
        "      model=model,\n",
        "      clock_now=clock.now,\n",
        "      memory=mem,\n",
        "      timeframe_delta_from=datetime.timedelta(hours=4),\n",
        "      timeframe_delta_until=datetime.timedelta(hours=1),\n",
        "      components=[identity],\n",
        "      component_name='summary of observations',\n",
        "  )\n",
        "\n",
        "\n",
        "  goal_metric = goal_achievement.GoalAchievementMetric(\n",
        "      model=model,\n",
        "      player_name=agent_config.name,\n",
        "      player_goal=agent_config.goal,\n",
        "      clock=clock,\n",
        "      name='Goal Achievement',\n",
        "      measurements=measurements,\n",
        "      channel='goal_achievement',\n",
        "      verbose=False,\n",
        "  )\n",
        "  morality_metric = common_sense_morality.CommonSenseMoralityMetric(\n",
        "      model=model,\n",
        "      player_name=agent_config.name,\n",
        "      clock=clock,\n",
        "      name='Morality',\n",
        "      verbose=False,\n",
        "      measurements=measurements,\n",
        "      channel='common_sense_morality',\n",
        "  )\n",
        "\n",
        "  agent = basic_agent.BasicAgent(\n",
        "      model,\n",
        "      agent_name=agent_config.name,\n",
        "      clock=clock,\n",
        "      verbose=True,\n",
        "      components=[\n",
        "          identity,\n",
        "          plan,\n",
        "          reflection,\n",
        "          time,\n",
        "          summary_obs,\n",
        "          current_obs,\n",
        "          goal_metric,\n",
        "          morality_metric,\n",
        "      ],\n",
        "  )\n",
        "\n",
        "  reputation_metric = opinion_of_others.OpinionOfOthersMetric(\n",
        "      model=model,\n",
        "      player_name=agent_config.name,\n",
        "      player_names=player_names,\n",
        "      context_fn=agent.state,\n",
        "      clock=clock,\n",
        "      name='Opinion',\n",
        "      verbose=False,\n",
        "      measurements=measurements,\n",
        "      channel='opinion_of_others',\n",
        "      question=\"What is {opining_player}'s opinion of {of_player}?\",\n",
        "  )\n",
        "\n",
        "  agent.add_component(reputation_metric)\n",
        "\n",
        "  return agent, mem"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "qt8CK2mMbD7q"
      },
      "cell_type": "markdown",
      "source": [
        "## Configure and build the players"
      ]
    },
    {
      "metadata": {
        "id": "QhAkMajsunp4"
      },
      "cell_type": "code",
      "source": [
        "NUM_PLAYERS = 5\n",
        "\n",
        "def make_random_big_five()-\u003estr:\n",
        "  return str({\n",
        "      'extraversion': random.randint(1, 10),\n",
        "      'neuroticism': random.randint(1, 10),\n",
        "      'openness': random.randint(1, 10),\n",
        "      'conscientiousness': random.randint(1, 10),\n",
        "      'agreeableness': random.randint(1, 10),\n",
        "  })\n",
        "\n",
        "scenario_premise = [\n",
        "\n",
        "    (\n",
        "        'Alice, Bob, Charlie and Dorothy are at the Sundrop Saloon. There '\n",
        "        + 'is a snow storm and they have to wait it out inside.'\n",
        "    ),\n",
        "]\n",
        "player_configs = [\n",
        "    formative_memories.AgentConfig(\n",
        "        name='Alice',\n",
        "        gender='female',\n",
        "        goal='Organise a street party in Riverbend.',\n",
        "        context=shared_context+'Alice is very socially active and knows everyone in town',\n",
        "        traits = make_random_big_five()\n",
        "            ),\n",
        "    formative_memories.AgentConfig(\n",
        "        name='Bob',\n",
        "        gender='male',\n",
        "        goal='Start a chess club in Riverbend.',\n",
        "        context=shared_context + 'Bob is a chess enthusiast',\n",
        "        traits = make_random_big_five()\n",
        "            ),\n",
        "    formative_memories.AgentConfig(\n",
        "        name='Charlie',\n",
        "        gender='male',\n",
        "        goal='Organise an ale festival at the Sundrop Saloon.',\n",
        "        context=shared_context + 'Charlie works at the Sundrop Saloon and loves real ales',\n",
        "        traits = make_random_big_five()\n",
        "            ),\n",
        "    formative_memories.AgentConfig(\n",
        "        name='Dorothy',\n",
        "        gender='female',\n",
        "        goal=(\n",
        "            'Take students on a tour of Riverbend'\n",
        "            ' it is funny.'\n",
        "        ),\n",
        "        context=shared_context + 'Dorothy is a teacher at school in Riverbend',\n",
        "        traits = make_random_big_five()\n",
        "            ),\n",
        "    formative_memories.AgentConfig(\n",
        "        name='Ellen',\n",
        "        gender='female',\n",
        "        goal=(\n",
        "            'Write a paper on the history of Riverbend.'\n",
        "        ),\n",
        "        context=shared_context + 'Ellen is a librarian in the library in Riverbend',\n",
        "        traits = make_random_big_five()\n",
        "    ),\n",
        "]"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "5RU3ZV4oIknW"
      },
      "cell_type": "code",
      "source": [
        "player_configs = player_configs[:NUM_PLAYERS]\n",
        "player_names = [player.name for player in player_configs][:NUM_PLAYERS]\n",
        "measurements = measurements_lib.Measurements()\n",
        "\n",
        "players = []\n",
        "memories = {}\n",
        "\n",
        "with concurrent.futures.ThreadPoolExecutor(max_workers=NUM_PLAYERS) as pool:\n",
        "  for agent, mem in pool.map(build_agent,\n",
        "                             player_configs[:NUM_PLAYERS],\n",
        "                             # All players get the same `player_names`.\n",
        "                             [player_names] * NUM_PLAYERS,\n",
        "                             # All players get the same `measurements` object.\n",
        "                             [measurements] * NUM_PLAYERS):\n",
        "    players.append(agent)\n",
        "    memories[agent.name] = mem\n"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "2vt8ggYUrW8M"
      },
      "cell_type": "markdown",
      "source": [
        "## Build GM"
      ]
    },
    {
      "metadata": {
        "id": "3W65kHOKQwrv"
      },
      "cell_type": "code",
      "source": [
        "game_master_memory = associative_memory.AssociativeMemory(\n",
        "    embedder, importance_model_gm.importance, clock=clock.now)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "bGNY_D7FID4I"
      },
      "cell_type": "code",
      "source": [],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "-cxivChc633z"
      },
      "cell_type": "code",
      "source": [
        "# @title Create components and externalities\n",
        "citizen_names = [player.name for player in players]\n",
        "player_names = [player.name for player in players]\n",
        "\n",
        "facts_on_village = generic_components.constant.ConstantComponent(\n",
        "    ' '.join(shared_memories), 'General knowledge of Riverbend'\n",
        ")\n",
        "player_status = gm_components.player_status.PlayerStatus(\n",
        "    clock.now, model, game_master_memory, player_names\n",
        ")\n",
        "\n",
        "relevant_events = gm_components.relevant_events.RelevantEvents(\n",
        "    clock.now, model, game_master_memory\n",
        ")\n",
        "time_display = gm_components.time_display.TimeDisplay(clock)\n",
        "\n",
        "\n",
        "convo_externality = gm_components.conversation.Conversation(\n",
        "    players,\n",
        "    model,\n",
        "    clock=clock,\n",
        "    memory=game_master_memory,\n",
        "    burner_memory_factory=blank_memory_factory,\n",
        "    components=[player_status],\n",
        "    cap_nonplayer_characters=2,\n",
        "    shared_context=shared_context,\n",
        "    verbose=False,\n",
        ")\n",
        "\n",
        "direct_effect_externality = gm_components.direct_effect.DirectEffect(\n",
        "    players,\n",
        "    model=model,\n",
        "    memory=game_master_memory,\n",
        "    clock_now=clock.now,\n",
        "    verbose=False,\n",
        "    components=[player_status],\n",
        ")"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "d_R2BVNOsAwa"
      },
      "cell_type": "code",
      "source": [
        "# @title Create the game master object\n",
        "env = game_master.GameMaster(\n",
        "    model=model,\n",
        "    memory=game_master_memory,\n",
        "    clock=clock,\n",
        "    players=players,\n",
        "    components=[\n",
        "        facts_on_village,\n",
        "        player_status,\n",
        "        convo_externality,\n",
        "        direct_effect_externality,\n",
        "        relevant_events,\n",
        "        time_display,\n",
        "    ],\n",
        "    randomise_initiative=True,\n",
        "    player_observes_event=False,\n",
        "    verbose=True,\n",
        ")"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "d2u0bQ1MSCGd"
      },
      "cell_type": "markdown",
      "source": [
        "## The RUN"
      ]
    },
    {
      "metadata": {
        "id": "hdTRDaxEZZnN"
      },
      "cell_type": "code",
      "source": [
        "clock.set(START_TIME)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "9IggLF1aH_hF"
      },
      "cell_type": "code",
      "source": [
        "#@title Initial observations and player location\n",
        "for player in players:\n",
        "  player.observe(\n",
        "      f'{player.name} is at home, they have just woken up.'\n",
        "  )\n",
        "  game_master_memory.add(f'{player.name} is at their private home.')\n"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "2Bt87stq76gF"
      },
      "cell_type": "code",
      "source": [
        "# @title Expect about 2-3 minutes per step.\n",
        "episode_length = 12  # @param {type: 'integer'}\n",
        "for _ in range(episode_length):\n",
        "  env.step()\n"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "DnwvpvQ4bnFs"
      },
      "cell_type": "markdown",
      "source": [
        "## Summary and analysis of the episode"
      ]
    },
    {
      "metadata": {
        "id": "5U5FDXvs4HSr"
      },
      "cell_type": "code",
      "source": [
        "# @title Metrics plotting\n",
        "\n",
        "group_by = collections.defaultdict(lambda: 'player')\n",
        "group_by['opinion_of_others'] = 'of_player'\n",
        "\n",
        "available_channels = list(measurements.available_channels())\n",
        "\n",
        "fig, ax = plt.subplots(1, len(available_channels), figsize=(6, 2))\n",
        "tb = [channel for channel in available_channels]\n",
        "for idx, channel in enumerate(available_channels):\n",
        "  plotting.plot_line_measurement_channel(measurements, channel,\n",
        "                                         group_by=group_by[channel],\n",
        "                                         xaxis='time_str',\n",
        "                                         ax=ax[idx])\n",
        "  ax[idx].set_title(channel)\n",
        "\n",
        "fig.set_constrained_layout(constrained=True)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "O4jp0xGXvOAJ"
      },
      "cell_type": "code",
      "source": [
        "# @title Summarize the entire story.\n",
        "all_gm_memories = env._memory.retrieve_recent(k=10000, add_time=True)\n",
        "\n",
        "detailed_story = '\\n'.join(all_gm_memories)\n",
        "print('len(detailed_story): ', len(detailed_story))\n",
        "# print(detailed_story)\n",
        "\n",
        "episode_summary = model.sample_text(\n",
        "    f'Sequence of events:\\n{detailed_story}'+\n",
        "    '\\nNarratively summarize the above temporally ordered ' +\n",
        "    'sequence of events. Write it as a news report. Summary:\\n',\n",
        "     max_tokens=3500, terminators=())\n",
        "print(episode_summary)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "ALG987t-6j-V"
      },
      "cell_type": "code",
      "source": [
        "# @title Summarise the perspective of each player\n",
        "player_logs = []\n",
        "player_log_names = []\n",
        "for player in players:\n",
        "  name = player.name\n",
        "  detailed_story = '\\n'.join(memories[player.name].retrieve_recent(\n",
        "      k=1000, add_time=True))\n",
        "  summary = ''\n",
        "  summary = model.sample_text(\n",
        "      f'Sequence of events that happened to {name}:\\n{detailed_story}'\n",
        "      '\\nWrite a short story that summarises these events.\\n'\n",
        "      ,\n",
        "       max_tokens=3500, terminators=())\n",
        "\n",
        "  all_player_mem = memories[player.name].retrieve_recent(k=1000, add_time=True)\n",
        "  all_player_mem = ['Summary:', summary, 'Memories:'] + all_player_mem\n",
        "  player_html = html_lib.PythonObjectToHTMLConverter(all_player_mem).convert()\n",
        "  player_logs.append(player_html)\n",
        "  player_log_names.append(f'{name}')\n"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "UmPOvjVxddye"
      },
      "cell_type": "markdown",
      "source": [
        "#Build and display HTML log of the experiment"
      ]
    },
    {
      "metadata": {
        "id": "JyEoGgI05xI0"
      },
      "cell_type": "code",
      "source": [
        "history_sources = [env, direct_effect_externality, convo_externality]\n",
        "histories_html = [html_lib.PythonObjectToHTMLConverter(history.get_history()).convert() for history in history_sources]\n",
        "histories_names = [history.name for history in history_sources]"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "XNJuo4Dwt5Ui"
      },
      "cell_type": "code",
      "source": [
        "gm_mem_html = html_lib.PythonObjectToHTMLConverter(all_gm_memories).convert()\n",
        "\n",
        "tabbed_html = html_lib.combine_html_pages(\n",
        "    histories_html + [gm_mem_html] + player_logs,\n",
        "    histories_names + ['GM'] + player_log_names,\n",
        "    summary=episode_summary,\n",
        "    title='Riverbend elections experiment',\n",
        ")\n",
        "\n",
        "tabbed_html = html_lib.finalise_html(tabbed_html)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "pumxvmrzANOq"
      },
      "cell_type": "code",
      "source": [
        "display.HTML(tabbed_html)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "HX-M9Im_dneG"
      },
      "cell_type": "markdown",
      "source": [
        "#Interact with a specific player"
      ]
    },
    {
      "metadata": {
        "id": "ESJ1l7_Kt5Uj"
      },
      "cell_type": "code",
      "source": [
        "sim_to_interact = 'Alice'  # @param ['Alice', 'Bob','Charlie', 'Dorothy', 'Ellen'] {type:\"string\"}\n",
        "user_identity = 'a close friend'  # @param {type:\"string\"}\n",
        "interaction_premise = f'{sim_to_interact} is talking to {user_identity}\\n'  # @param {type:\"string\"}\n",
        "\n",
        "player_names = [player.name for player in players]\n",
        "player_by_name = {player.name: player for player in players}\n",
        "selected_player = player_by_name[sim_to_interact]\n",
        "interrogation = interaction_premise"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "5Q1cYflLt5Uj"
      },
      "cell_type": "code",
      "source": [
        "utterence_from_user = 'Did you win the elections?'  # @param {type:\"string\"}\n",
        "\n",
        "interrogation += f'{user_identity}: {utterence_from_user}'\n",
        "player_says = selected_player.say(interrogation)\n",
        "interrogation += f'\\n{sim_to_interact}: {player_says}\\n'\n",
        "print(interrogation)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "wp-0nqnW8Ay8"
      },
      "cell_type": "markdown",
      "source": [
        "```\n",
        "Copyright 2023 DeepMind Technologies Limited.\n",
        "\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "you may not use this file except in compliance with the License.\n",
        "You may obtain a copy of the License at\n",
        "\n",
        "    https://www.apache.org/licenses/LICENSE-2.0\n",
        "\n",
        "Unless required by applicable law or agreed to in writing, software\n",
        "distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "See the License for the specific language governing permissions and\n",
        "limitations under the License.\n",
        "```"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "last_runtime": {
        "build_target": "",
        "kind": "private"
      },
      "private_outputs": true,
      "provenance": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
